# This is a slightly modified copy from pyexpat.py from FePy distribution
# http://fepy.svn.sourceforge.net/viewvc/fepy/trunk/lib/pyexpat.py?revision=614&view=markup
# Modifications:
# - Inline the copyright message.
# - No need to reference the System.Xml assembly.

# Copyright (c) 2005-2007 Seo Sanghyeon and others

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

# A chapter from Dan Wahlin's "XML for ASP.NET Developers" is useful
# for understanding this code. Posted to informit.com, 2002-02-22.
# http://www.informit.com/articles/article.asp?p=25485 

# 2005-11-16 sanxiyn Created
# 2006-08-18 sanxiyn Merged changes from Mark Rees
#  * Adapted to the new way to load .NET libraries
#  * Handle empty elements
# 2006-08-29 sanxiyn Added support for XML namespaces
#                    Simplified code a lot
# 2006-10-21 sanxiyn Minimal support for xml.sax
# 2006-10-24 sanxiyn Implemented ordered_attributes, namespace_prefixes
# 2006-10-27 sanxiyn Added expat.error
# 2006-10-29 sanxiyn Implemented Start/End NamespaceDeclHandler
# 2006-11-20 sanxiyn Merged changes from Fredrik Lundh
#  * Handle multiple calls to Parse()
# 2007-02-19 sylvain Added missing EndDoctypeDeclHandler declaration
# 2007-10-11 shozoa  Set ProhibitDtd to false
# 2007-12-18 sanxiyn Made returns_unicode settable (for gnosis.xml.objectify)
#                    Implemented ParseFile()

# Modification for CoDeSys: "import clr" is forbidden, but luckily, System.Xml
# is referenced by default since CoDeSys V3.5
# import clr
# clr.AddReference("System.Xml")

from System import Enum
from System.IO import StringReader, TextReader
from System.Xml import XmlReader, XmlReaderSettings, XmlNodeType, ValidationType

# Adaptor class from Python file to TextReader

class PythonFileReader(TextReader):

    def __init__(self, f):
        self.f = f

    def Read(self, buffer, index, count):
        chars = self.f.read(count).ToCharArray()
        chars.CopyTo(buffer, index)
        return len(chars)

# xml.sax passes an undocumented keyword argument "intern" to ParserCreate.
# Let's ignore it.

def ParserCreate(encoding=None, namespace_separator=None, **kw):
    return xmlparser(namespace_separator)

# Used by xml.sax
XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE = 1
# Used by Kid
XML_PARAM_ENTITY_PARSING_ALWAYS = 2

class ExpatError(Exception):
    pass

error = ExpatError

def ErrorString(errno):
    return 'unknown error'

class xmlparser(object):

    __slots__ = [
        # Internal
        "_data",
        "_separator",
        "_reader",
        "_ns_stack",

        # Attributes
        # Implemented
        "ordered_attributes",
        "namespace_prefixes",
        "returns_unicode",
        # Stub for xml.dom
        "buffer_text",
        "specified_attributes",

        # Handlers
        # Implemented
        "StartElementHandler",
        "EndElementHandler",
        "CharacterDataHandler",
        "StartNamespaceDeclHandler",
        "EndNamespaceDeclHandler",
        # Stub for ElementTree
        "DefaultHandlerExpand",
        # Stub for xml.sax
        "ProcessingInstructionHandler",
        "UnparsedEntityDeclHandler",
        "NotationDeclHandler",
        "ExternalEntityRefHandler",
        "EndDoctypeDeclHandler",
        # Stub for xml.dom
        "StartDoctypeDeclHandler",
        "EntityDeclHandler",
        "CommentHandler",
        "StartCdataSectionHandler",
        "EndCdataSectionHandler",
        "XmlDeclHandler",
        "ElementDeclHandler",
        "AttlistDeclHandler",
        # Stub for Kid
        "DefaultHandler",
        ]

    intern = {}
    
    def _get_CurrentLineNumber(self):
        return self._reader.LineNumber
    CurrentLineNumber = property(_get_CurrentLineNumber)
    
    def _get_CurrentColumnNumber(self):
        return self._reader.LinePosition
    CurrentColumnNumber = property(_get_CurrentColumnNumber)
    
    def __init__(self, separator):
        self._data = []
        self._separator = separator
        self._ns_stack = []
        self.ordered_attributes = False
        self.namespace_prefixes = False
        self.returns_unicode = True

    def Parse(self, data, isfinal=False):
        self._data.append(data)
        if isfinal:
            data = "".join(self._data)
            self._data = None
            reader = StringReader(data)
            self._parse(reader)

    def ParseFile(self, file):
        reader = PythonFileReader(file)
        self._parse(reader)

    def _qname(self):
        separator = self._separator
        reader = self._reader
        if separator is None:
            return reader.Name
        if reader.NamespaceURI:
            temp = reader.NamespaceURI + separator + reader.LocalName
            if self.namespace_prefixes:
                if reader.Prefix:
                    return temp + separator + reader.Prefix
            return temp
        else:
            return reader.LocalName

    def _parse(self, reader):
        settings = XmlReaderSettings(ProhibitDtd=False)
        reader = XmlReader.Create(reader, settings)
        self._reader = reader
        while reader.Read():
            nodetype = reader.NodeType
            typename = Enum.GetName(XmlNodeType, nodetype)
            handler = getattr(self, '_handle_' + typename, None)
            if handler is not None:
                handler()

    def _handle_Element(self):
        reader = self._reader
        name = self._qname()
        ns_stack = self._ns_stack
        ns_stack.append(None)
        if self.ordered_attributes:
            attributes = []
        else:
            attributes = {}
        while reader.MoveToNextAttribute():
            if reader.Prefix == 'xmlns':
                prefix = reader.LocalName
                uri = reader.Value
                ns_stack.append(prefix)
                if hasattr(self, "StartNamespaceDeclHandler"):
                    self.StartNamespaceDeclHandler(prefix, uri)
                continue
            key = self._qname()
            value = reader.Value
            if self.ordered_attributes:
                attributes.append(key)
                attributes.append(value)
            else:
                attributes[key] = value
        reader.MoveToElement()
        if hasattr(self, "StartElementHandler"):
            self.StartElementHandler(name, attributes)
        # EndElement node is not generated for empty elements.
        # Call its handler here.
        if reader.IsEmptyElement:
            self._handle_EndElement()

    def _handle_EndElement(self):
        name = self._qname()
        if hasattr(self, "EndElementHandler"):
            self.EndElementHandler(name)
        ns_stack = self._ns_stack
        while True:
            prefix = ns_stack.pop()
            if prefix is None:
                break
            if hasattr(self, "EndNamespaceDeclHandler"):
                self.EndNamespaceDeclHandler(prefix)

    def _handle_Text(self):
        reader = self._reader
        data = reader.Value
        if hasattr(self, "CharacterDataHandler"):
            self.CharacterDataHandler(data)
    _handle_CDATA = _handle_Text

    # Stub for xml.sax
    def SetBase(self, base):
        pass
    def SetParamEntityParsing(self, flag):
        return True
    # Stub for Kid
    def UseForeignDTD(self):
        pass
